package com.ruoyi.common;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONException;
import org.apache.http.*;
import org.apache.http.client.CircularRedirectException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 增强版HTTP工具类（支持GET/POST/文件上传/JSON）
 */
public class HttpClient {
    private static final Logger log = LoggerFactory.getLogger(HttpClient.class);
    private static final int DEFAULT_TIMEOUT_MS = 100_000;
    private static final int MAX_TOTAL_CONNECTIONS = 200;
    private static final int MAX_PER_ROUTE = 50;
    private static final int VALIDATE_AFTER_INACTIVITY_MS = 30_000;
    // 修改static初始化块
    static {
        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
        connManager.setMaxTotal(MAX_TOTAL_CONNECTIONS);
        connManager.setDefaultMaxPerRoute(MAX_PER_ROUTE);
        connManager.setValidateAfterInactivity(VALIDATE_AFTER_INACTIVITY_MS);

        RequestConfig defaultRequestConfig = RequestConfig.custom()
                .setConnectTimeout(DEFAULT_TIMEOUT_MS)
                .setSocketTimeout(DEFAULT_TIMEOUT_MS)
                .setConnectionRequestTimeout(DEFAULT_TIMEOUT_MS)
                .build();

        try {
            SSLContext sslContext = new SSLContextBuilder()
                    .loadTrustMaterial(null, (chain, authType) -> true)
                    .build();

            httpClient = HttpClients.custom()
                    .setConnectionManager(connManager)
                    .setDefaultRequestConfig(defaultRequestConfig)
                    .setSSLContext(sslContext)
                    .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                    // 关键修改：添加重定向策略
                    .setRedirectStrategy(new PostRedirectStrategy())
                    // 允许循环重定向（最多5次）
                    .setRedirectStrategy(new DefaultRedirectStrategy() {
                        private final PostRedirectStrategy postStrategy = new PostRedirectStrategy();
                        private int redirectCount = 0;

                        @Override
                        public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) throws CircularRedirectException {
                            boolean isRedirect = postStrategy.isRedirected(request, response, context);
                            if (isRedirect && redirectCount >= 5) {
                                throw new CircularRedirectException("Too many redirects");
                            }
                            return isRedirect;
                        }

                        @Override
                        public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException {
                            redirectCount++;
                            return postStrategy.getRedirect(request, response, context);
                        }
                    })
                    .evictExpiredConnections()
                    .evictIdleConnections(60, TimeUnit.SECONDS)
                    .build();
        } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
            throw new RuntimeException("初始化HttpClient失败", e);
        }
    }
    // 使用连接池的HttpClient实例
    private static CloseableHttpClient httpClient;

    static {
        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
        connManager.setMaxTotal(MAX_TOTAL_CONNECTIONS);
        connManager.setDefaultMaxPerRoute(MAX_PER_ROUTE);
        connManager.setValidateAfterInactivity(VALIDATE_AFTER_INACTIVITY_MS);

        RequestConfig defaultRequestConfig = RequestConfig.custom()
                .setConnectTimeout(DEFAULT_TIMEOUT_MS)
                .setSocketTimeout(DEFAULT_TIMEOUT_MS)
                .setConnectionRequestTimeout(DEFAULT_TIMEOUT_MS)
                .build();

        try {
            SSLContext sslContext = new SSLContextBuilder()
                    .loadTrustMaterial(null, (chain, authType) -> true)
                    .build();

            httpClient = HttpClients.custom()
                    .setConnectionManager(connManager)
                    .setDefaultRequestConfig(defaultRequestConfig)
                    .setSSLContext(sslContext)
                    .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                    .evictExpiredConnections()
                    .evictIdleConnections(60, TimeUnit.SECONDS)
                    .build();
        } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
            throw new RuntimeException("初始化HttpClient失败", e);
        }
    }

    // ------------------- POST请求（表单/JSON/文件） -------------------

    public static String doPostForm(String url, Map<String, String> formParams) throws IOException {
        return doPostForm(url, formParams, defaultConfig());
    }

    public static String doPostForm(String url, Map<String, String> formParams, RequestConfig config) throws IOException {
        HttpPost httpPost = buildFormPost(url, formParams);
        httpPost.setConfig(config);
        return executeRequest(httpPost);
    }

    public static String doPostJson(String url, Object jsonBody) throws IOException {
        return doPostJson(url, jsonBody, defaultConfig());
    }

    public static String doPostJson(String url, Object jsonBody, RequestConfig config) throws IOException {
        HttpPost httpPost = buildJsonPost(url, jsonBody);
        httpPost.setConfig(config);
        return executeRequest(httpPost);
    }

    public static String doPostWithFiles(String url, Map<String, String> formParams,
                                         Map<String, File> fileParams) throws IOException {
        return doPostWithFiles(url, formParams, fileParams, defaultConfig());
    }

    public static String doPostWithFiles(String url, Map<String, String> formParams,
                                         Map<String, File> fileParams, RequestConfig config) throws IOException {
        HttpPost httpPost = buildMultipartPost(url, formParams, fileParams);
        httpPost.setConfig(config);
        return executeRequest(httpPost);
    }

    // ------------------- 请求构建 -------------------

    private static HttpPost buildFormPost(String url, Map<String, String> formParams) {
        HttpPost httpPost = new HttpPost(url);
        if (formParams != null && !formParams.isEmpty()) {
            List<NameValuePair> params = new ArrayList<>(formParams.size());
            formParams.forEach((k, v) -> params.add(new BasicNameValuePair(k, v)));
            httpPost.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));
        }
        return httpPost;
    }

    private static HttpPost buildJsonPost(String url, Object jsonBody) throws IOException {
        HttpPost httpPost = new HttpPost(url);
        if (jsonBody != null) {
            try {
                String jsonString = jsonBody instanceof String ?
                        (String) jsonBody : JSON.toJSONString(jsonBody);
                httpPost.setEntity(new StringEntity(jsonString, ContentType.APPLICATION_JSON));
            } catch (JSONException e) {
                throw new IOException("JSON序列化失败", e);
            }
        }
        return httpPost;
    }

    private static HttpPost buildMultipartPost(String url, Map<String, String> formParams,
                                               Map<String, File> fileParams) throws IOException {
        HttpPost httpPost = new HttpPost(url);
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();

        if (fileParams != null && !fileParams.isEmpty()) {
            fileParams.forEach((paramName, file) -> {
                try {
                    ContentType contentType = ContentType.create(
                            Files.probeContentType(file.toPath()),
                            StandardCharsets.UTF_8
                    );
                    builder.addPart(paramName, new FileBody(file, contentType, file.getName()));
                } catch (IOException e) {
                    throw new RuntimeException("检测文件类型失败: " + file.getName(), e);
                }
            });
        }

        if (formParams != null && !formParams.isEmpty()) {
            formParams.forEach((key, value) -> builder.addTextBody(
                    key,
                    value,
                    ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8)
            ));
        }

        httpPost.setEntity(builder.build());
        return httpPost;
    }

    // ------------------- 请求执行 -------------------

    private static final int MAX_REDIRECTS = 5; // 最大重定向次数

    private static String executeRequest(HttpUriRequest originalRequest) throws IOException {
        return executeRequest(originalRequest, 0);
    }

    private static String executeRequest(HttpUriRequest request, int redirectCount) throws IOException {
        if (redirectCount > MAX_REDIRECTS) {
            throw new IOException("超过最大重定向次数: " + MAX_REDIRECTS);
        }

        log.debug("执行请求: {} {}", request.getMethod(), request.getURI());

        try (CloseableHttpResponse response = httpClient.execute(request)) {
            int statusCode = response.getStatusLine().getStatusCode();
            Header locationHeader = response.getFirstHeader("Location");

            // 处理重定向 (301, 302, 303, 307, 308)
            if (isRedirect(statusCode) && locationHeader != null) {
                String newLocation = locationHeader.getValue();
                log.debug("重定向到: {}", newLocation);

                // 构建新请求
                HttpUriRequest newRequest = buildRedirectRequest(request, newLocation, statusCode);
                EntityUtils.consumeQuietly(response.getEntity()); // 释放资源

                // 递归调用
                return executeRequest(newRequest, redirectCount + 1);
            }

            // 处理正常响应
            HttpEntity entity = response.getEntity();
            String responseBody = null;

            try {
                if (entity != null) {
                    responseBody = EntityUtils.toString(entity, StandardCharsets.UTF_8);
                }
            } finally {
                EntityUtils.consumeQuietly(entity);
            }

            if (statusCode < 200 || statusCode >= 300) {
                throw new IOException(String.format(
                        "HTTP请求失败，状态码: %d，响应内容: %s",
                        statusCode,
                        responseBody != null ? responseBody : "<空>"
                ));
            }

            return responseBody;
        } catch (IOException e) {
            log.error("HTTP请求执行失败: {}", request.getURI(), e);
            throw e;
        }
    }

    // 判断是否为需要处理的重定向状态码
    private static boolean isRedirect(int statusCode) {
        return statusCode == HttpStatus.SC_MOVED_PERMANENTLY   // 301
                || statusCode == HttpStatus.SC_MOVED_TEMPORARILY    // 302
                || statusCode == HttpStatus.SC_SEE_OTHER            // 303
                || statusCode == HttpStatus.SC_TEMPORARY_REDIRECT; // 307
    }

    // 构建重定向请求
    private static HttpUriRequest buildRedirectRequest(HttpUriRequest original, String newLocation, int statusCode)
            throws IOException {

        URI newUri;
        try {
            newUri = new URI(newLocation).normalize();
        } catch (URISyntaxException e) {
            throw new IOException("无效的重定向地址: " + newLocation, e);
        }

        // 创建新请求
        RequestBuilder requestBuilder = RequestBuilder.copy(original)
                .setUri(newUri)
                .removeHeaders("Content-Length"); // 移除自动生成的Content-Length

        // 处理POST重定向时需要保存请求体
        if (statusCode == HttpStatus.SC_TEMPORARY_REDIRECT &&
                original instanceof HttpEntityEnclosingRequest) {
            HttpEntity entity = ((HttpEntityEnclosingRequest) original).getEntity();
            if (entity != null && !entity.isRepeatable()) {
                // 将不可重复的实体转换为可重复的
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                entity.writeTo(baos);
                requestBuilder.setEntity(new ByteArrayEntity(baos.toByteArray()));
            }
        }

        return requestBuilder.build();
    }

    // ------------------- 文件下载 -------------------

    public static void downloadFile(String fileURL, String savePath) throws IOException {
        URL url = new URL(fileURL);
        HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();

        try {
            httpConn.setRequestMethod("GET");
            httpConn.setRequestProperty("User-Agent", "Mozilla/5.0");
            httpConn.setConnectTimeout(30_000);
            httpConn.setReadTimeout(60_000);
            httpConn.setInstanceFollowRedirects(false); // 手动处理重定向

            int status = httpConn.getResponseCode();
            if (status == HttpURLConnection.HTTP_MOVED_PERM
                    || status == HttpURLConnection.HTTP_MOVED_TEMP) {
                String newUrl = httpConn.getHeaderField("Location");
                downloadFile(newUrl, savePath);
                return;
            }

            if (status != HttpURLConnection.HTTP_OK) {
                throw new IOException("服务器返回非200状态码: " + status);
            }

            Path savePathObj = Paths.get(savePath);
            if (Files.isDirectory(savePathObj)) {
                throw new IOException("保存路径是目录: " + savePath);
            }

            Files.createDirectories(savePathObj.getParent());

            try (InputStream is = httpConn.getInputStream()) {
                Files.copy(is, savePathObj);
            }

        } finally {
            httpConn.disconnect();
        }
    }

    // ------------------- 工具方法 -------------------

    private static RequestConfig defaultConfig() {
        return RequestConfig.copy(RequestConfig.DEFAULT)
                .setConnectTimeout(DEFAULT_TIMEOUT_MS)
                .setSocketTimeout(DEFAULT_TIMEOUT_MS)
                .build();
    }
}